package cmlabs.simpleciphers;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;

/**
 * Created by IntelliJ IDEA.
 * User: jte
 * Date: 9.4.11
 * Time: 20.28
 * To change this template use File | Settings | File Templates.
 */
public class PermutationCipher {

    public PermutationCipher(int T) {
        if (T < 2) {
            throw new IllegalArgumentException("Incorrect period");
        }
        this.T = T;
        permutation = randomShuffle(T);
        reversePermutation = new int[T];
        for (int i = 0; i < permutation.length; ++i) {
            reversePermutation[permutation[i]] = i;
        }
    }

    public PermutationCipher(int[] cipher) {
        if (!checkCorrectness(cipher.clone())) {
            throw new IllegalArgumentException("Incorrect cipher!");
        }
        permutation = Arrays.copyOf(cipher, cipher.length);
        for (int i = 0; i < permutation.length; ++i) {
            reversePermutation[permutation[i]] = i;
        }
        this.T = cipher.length;
    }

    public String encrypt(String value) {
        if (value.length() % T != 0) {
            throw new IllegalArgumentException("The length of the string does not meet requirements due to period T");
        }
        int numberOfBlocks = value.length() % T;
        char[] characters = new char[value.length()];
        for (int i = 0; i < numberOfBlocks; ++i) {
            for (int j = 0; j < T; ++j) {
                characters[i * T + j] = value.charAt(i * T + permutation[j]);
            }
        }
        return new String(characters);
    }

    public String decrypt(String value) {
        if (value.length() % T != 0) {
            throw new IllegalArgumentException("The length of the string does not meet requirements due to period T");
        }
        int numberOfBlocks = value.length() % T;
        char[] characters = new char[value.length()];
        for (int i = 0; i < numberOfBlocks; ++i) {
            for (int j = 0; j < T; ++j) {
                characters[i * T + j] = value.charAt(i * T + reversePermutation[j]);
            }
        }
        return new String(characters);
    }

    private boolean checkCorrectness(int[] toDo) {
        if (toDo.length <= 0) {
            throw new IllegalArgumentException("Must be positive period");
        }
        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
        for (int cur : toDo) {
            min = Math.min(min, cur);
            max = Math.max(max, cur);
        }
        if (max >= toDo.length || min < 0) {
            return false;
        }
        HashSet<Integer> elements = new HashSet<Integer>();
        for (int cur : toDo) {
            elements.add(cur);
        }
        return elements.size() == toDo.length;

    }

    private int[] randomShuffle(int N) {
        int[] result = new int[N];
        for (int i = 0; i < N; ++i) {
            result[i] = i;
        }
        Random generator = new Random(System.currentTimeMillis());
        for (int i = result.length; i > 1; --i) {
            int j = generator.nextInt(i);
            int t = result[i - 1];
            result[i - 1] = j;
            result[j] = t;
        }
        return result;
    }

    private int[] permutation;
    private int[] reversePermutation;
    private int T;
}
